/*
	This file is part of the JKTest
    Copyright (C) 2008 Royce Y. Feng <ryfeng@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License version 3
    as published by the Free Software Foundation. You may not use, modify
    or distribute this program under any other version of the
    GNU Affero General Public License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package jktest;

import javax.swing.UIManager;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.GridLayout;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import javax.swing.JOptionPane;
import javax.swing.BorderFactory;
import javax.swing.SwingUtilities;
import javax.swing.BoxLayout;
import java.util.HashMap;

import handlers.JKTestHandler;
import GUIExtras.MainMenuBar;
import GUIExtras.JKSystemTray;
import JKAboutMe.AboutMeGUI;
import JKSysInfo.JKSysInfoGUI;
import AdvancedOptions.AdvancedOptionsGUI;
import org.hyperic.sigar.Sigar;

// Since I know i'll use all of them...
import JKPanels.*;
// Since I know i'll use both
import JKSerialization.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import org.hyperic.sigar.CpuInfo;
import org.hyperic.sigar.SigarException;
import org.hyperic.sigar.cmd.Shell;

/**
 * 
 * The main class of JKTest. This holds all the tests and other functions that
 * do not belong to the auxilary panels. Panels are added to the JTabbedPane 
 * in order to do the tests.
 * 
 * @author Royce Feng
 */

public class JKMainGui {
    private JFrame frame = new JFrame();
    private JPanel mainpanel = new JPanel();
    private JPanel panelT = new JPanel();
    private JPanel panelR = new JPanel( new GridLayout(1,3) );
    private JTabbedPane panel;
    private TestResultsPanel resultPane;
    private TestResultsPanel previousResultsPane;
    private MainMenuBar menuBar;

    // SystemTray class
    private JKSystemTray sysTray;

    // System Information Panel
    private JKSysInfoGUI systemInfo;
    
    // Test Handler
    private JKTestHandler handler;

    // About me panel GUI
    private AboutMeGUI aboutMe;

    // Advanced Options GUI
    private AdvancedOptionsGUI advancedOptions;
    
    // Test Panels
    private DevicePanel panel1;
    private PartitionPanel panel2;
    private EthernetPanel panel3;
    private SoundPanel panel4;
    private OpenOfficePanel panel5;
    private SoftwarePanel panel6;
    private FloppyPanel panel7;
    private ExtendedSoftwarePanel panel8;

    // Hashmap for Advanced Options
    private HashMap<String,JKMainPanel> componentMap = new HashMap<String,JKMainPanel>();
    private HashMap<String,Boolean> panelEnabled = new HashMap<String,Boolean>();

    // Array of panels
    private String[] panels = {"Devices","Ethernet","Floppy"/*,"OpenOffice"*/,"Partitions Check","Software",
            "Sound"};

    private boolean messageState = true;
    
    /**
     * 
     * Main Constructor for this class
     * 
     */
    public JKMainGui() {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } catch (Exception e) {
            // oh well
        }
        mainpanel.setLayout(new BoxLayout(mainpanel,BoxLayout.Y_AXIS));
        mainpanel.add(new JKTitlePanel());
        sysTray = new JKSystemTray( this );
        handler = new JKTestHandler(this, panels );
        menuBar = new MainMenuBar(this);
        frame.setJMenuBar(menuBar);
        frame.setContentPane(mainpanel);
        frame.setResizable ( false );  
        frame.setTitle("JKTest - Beta");
        panel = new JTabbedPane();
        panel.setPreferredSize(new Dimension(660,300));
        panel1 = new DevicePanel(this);
        panel2 = new PartitionPanel(this);
        panel3 = new EthernetPanel(this);
        panel4 = new SoundPanel(this);
        //panel5 = new OpenOfficePanel(this);
        panel6 = new SoftwarePanel(this);
        panel7 = new FloppyPanel(this);
        //panel8 = new ExtendedSoftwarePanel(this);
        panel.add(panel1);
        panel.add(panel4);
        //panel.add(panel5);
        panel.add(panel2);
        panel.add(panel3);
        panel.add(panel6);
        panel.add(panel7);
        //panel.add(panel8);
        panelT.add(panel);
        
        resultPane = new TestResultsPanel("Test Results");
        previousResultsPane = new TestResultsPanel("Previous Test Results");
        TestResultsPanel temp = JKObjectReader.readResults();
        if ( temp != null ) {
            previousResultsPane = temp;
            previousResultsPane.setBorder(BorderFactory.createTitledBorder("Previous Test Results"));
        } else {
            JOptionPane.showMessageDialog(frame, "This is either the first time JKTest has been started " +
                    "or the previous results were not saved properly. If the latter is the case, please contact " +
                    "the developer.");
        }
        systemInfo = new JKSysInfoGUI();
        panelR.add(resultPane);
        panelR.add(previousResultsPane);
        panelR.add(systemInfo);
        mainpanel.add(panelT);
        mainpanel.add(panelR);
        frame.setVisible ( true );
        frame.setDefaultCloseOperation ( JFrame.DO_NOTHING_ON_CLOSE );
        frame.addWindowListener(new WindowStalker());
        componentMap.put("devices", panel1);
        componentMap.put("partitions", panel2);
        componentMap.put("ethernet", panel3);
        componentMap.put("sound", panel4);
        //componentMap.put("openOffice", panel5);
        componentMap.put("software", panel6);
        componentMap.put("floppy", panel7);
        componentMap.put("extended", panel8);
        panelEnabled.put("devices", true);
        panelEnabled.put("partitions", true);
        panelEnabled.put("ethernet", true);
        panelEnabled.put("sound", true);
        //panelEnabled.put("openOffice", true);
        panelEnabled.put("software", true);
        panelEnabled.put("floppy", true);
        panelEnabled.put("extended", true);
        aboutMe = new AboutMeGUI();
        advancedOptions = new AdvancedOptionsGUI(this);
        setIconImage();
        frame.pack();
        checkCPUandMemory(loadHardwareMinimums());
    }
    
    @SuppressWarnings("CallToThreadDumpStack")
    private void checkCPUandMemory(Map<String, Integer> map) {
        try {
            Sigar sigar = (new Shell()).getSigar();
            CpuInfo[] infos = sigar.getCpuInfoList();
            CpuInfo info = infos[0];
            if (map.get("processor") >= info.getMhz() ) {
                JOptionPane.showMessageDialog(null, 
                        String.format("Processor minimum is %d Mhz but computer has %d Mhz", map.get("processor"), info.getMhz()), 
                        "Error", 
                        JOptionPane.ERROR_MESSAGE);
                System.out.println("processor: "+info.getMhz());
            }
            long memory = sigar.getMem().getTotal()/1024/1024;
            if ( map.get("memory") >= memory ) {
                JOptionPane.showMessageDialog(null, 
                        String.format("Memory minimum is %d Mb but computer has %d Mb", map.get("memory"), memory),
                        "Error", 
                        JOptionPane.ERROR_MESSAGE);
                System.out.println("memory: "+memory);
            }
        } catch (SigarException e) {
            e.printStackTrace();
        }
    }
    
    @SuppressWarnings("CallToThreadDumpStack")
    private Map<String, Integer> loadHardwareMinimums() {
        Map<String, Integer> map = new HashMap<String, Integer>();
        try {
            BufferedReader reader = new BufferedReader(new FileReader("hardware.csv"));
            String line;
            while ( (line = reader.readLine()) != null ) {
                String[] data = line.split(",");
                map.put(data[0], Integer.parseInt(data[1]) );
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        return map;
    }

    private void setIconImage() {
        Image img = sysTray.getImage();
        frame.setIconImage(img);
    }

    /**
     *
     * Method to allow turning on/off of panels
     *
     * @param keyname The name of the panel (hashed)
     * @param onOff True if the panel is to be turned on, False if the panel is to be turned off
     */
    public void togglePanel ( String keyname, boolean onOff ) {
        JKMainPanel toggled = componentMap.get(keyname);
        if ( panelEnabled.get(keyname) == onOff ) {
            // Do nothing
        } else {
            if (!onOff) {
                panel.remove(toggled);
            } else {
                panel.add(toggled,0);
            }
        }
        panelEnabled.put(keyname, onOff);
    }

    /**
     *
     * Method to determine whether a particular test has been enabled or disabled
     *
     * @param keyname The name of the panel (hashed)
     * @return True if the panel is enabled, False otherwise
     */
    public boolean panelEnabled( String keyname ) {
        return panelEnabled.get(keyname);
    }
    
    /**
     * 
     * A class that will handle window events. 
     * 
     */
    private class WindowStalker implements WindowListener {
        public void windowActivated( WindowEvent e ) {};
        public void windowClosed( WindowEvent e ) {
            System.exit(0);
        };
        public void windowDeactivated( WindowEvent e ) {};
        public void windowDeiconified( WindowEvent e ) {};
        public void windowIconified( WindowEvent e ) {};
        public void windowClosing( WindowEvent e ) {
            if (handler.CanClose()) {
                JKObjectWriter.writeResults(resultPane);
                sysTray.closeTray();
                System.exit(0);
            } else {
                int confirmation = JOptionPane.showConfirmDialog(null,"The results will be saved as is (not completed, failed" +
                        ", or passed). By clicking Ok, you forfeit the right to complain about this.","Confirmation",JOptionPane.OK_CANCEL_OPTION);
                if ( confirmation == JOptionPane.OK_OPTION ) {
                    JKObjectWriter.writeResults(resultPane);
                    sysTray.closeTray();
                    System.exit(0);
                } else {
                    JOptionPane.showMessageDialog(null, "I'm sorry, I can't allow you to exit if you don't understand the implications" +
                            " of abruptly exitting.");
                }
            }
        };
        public void windowOpened( WindowEvent e ) {};
    }
    
    /**
     * 
     * The function that will handle test pass/fails. This should never be  called
     * except by a panel that is in this Class.
     * 
     * @param source The source represented lexicographically
     * @param val The boolean value that answers the question: "Passed?"
     */
    public void valChanged ( String source, boolean val ) {
        String value = "";
        if ( val ) {
            value = "Passed";
        } else {
            value = "Failed";
        }
        handler.Hash(source, true);
        resultPane.valueChanged(source,value);
    }

    /**
     *
     * Allows for the tests of the program to be reset
     *
     * @param source The string representation of the test
     */
    public void resetValue ( String source ) {
        resultPane.valueChanged(source,"Not Completed.");
    }

    /**
     * 
     * Procedure handler for Menu
     * 
     * @param source String representing the MenuItem clicked
     */
    public void MenuItemClicked ( String source ) {
        if ( source.equals("Save Results") ) {
            JKObjectWriter.writeResults(resultPane);
        } else if (source.equals("Restart JKTest")) {
            int confirmation = JOptionPane.showConfirmDialog(null,"Do you really wish to restart (clear all the tests)?",
                    "Confirmation",JOptionPane.YES_NO_OPTION);
            if ( confirmation == JOptionPane.YES_OPTION ) {
                handler.restartHash(panels);
            }
        } else if (source.equals("About Me")) {
            aboutMe.toggleVisible();
        } else if (source.equals("Advanced Options")) {
            advancedOptions.toggleVisible();
        } else if (source.equals("Abruptly Exit")) {
            int confirmation = JOptionPane.showConfirmDialog(null,"The results will be saved as is (not completed, failed" +
                    ", or passed). By clicking Ok, you forfeit the right to complain about this.","Confirmation",JOptionPane.OK_CANCEL_OPTION);
            if ( confirmation == JOptionPane.OK_OPTION ) {
                JKObjectWriter.writeResults(resultPane);
                sysTray.closeTray();
                System.exit(0);
            } else {
                JOptionPane.showMessageDialog(null, "I'm sorry, I can't allow you to exit if you don't understand the implications" +
                        " of abruptly exitting.");
            }
        } else if (source.equals("Devices")) {
            panel.setSelectedComponent(panel1);
        } else if (source.equals("Ethernet")) {
            panel.setSelectedComponent(panel3);
        } else if (source.equals("Floppy")) {
            panel.setSelectedComponent(panel7);
        }/* else if (source.equals("OpenOffice")) {
            panel.setSelectedComponent(panel5);
        }*/ else if (source.equals("Partitions")) {
            panel.setSelectedComponent(panel2);
        } else if (source.equals("Software")) {
            panel.setSelectedComponent(panel6);
        } else if (source.equals("Sound")) {
            panel.setSelectedComponent(panel4);
        } else if ( source.equals("Run all Automated Tests") ) {
            panel2.ButtonClicked("Partitions Check");
            panel3.ButtonClicked("Automatic Test");
            panel6.ButtonClicked("Run Software");
            panel7.ButtonClicked("Floppy");
        } else if ( source.equals("Change Theme/UI") ) {
            new JKUIChooser(this);
        }
    }

    /**
     *
     * Updates the component tree UI of the frame and packs it in order to transition from one
     * theme to another
     *
     */
    public void packFrame() {
        SwingUtilities.updateComponentTreeUI(frame);
        frame.pack();
        aboutMe.pack();
        advancedOptions.pack();
    }

    /**
     *
     * Shows the next panel in the tabbed pane
     *
     */
    public void incPanel() {
        panel.setSelectedIndex((panel.getSelectedIndex()+1)%panel.getTabCount());
    }

    /**
     *
     * Sets the message state (true for showing messages, false for not)
     *
     * @param state
     */
    public void setMessageState( boolean state ) {
        messageState = state;
    }

    public boolean getMessageState () {
        return messageState;
    }

}
